package com.dpiinc.TechPage.oss

import android.util.Log
import android.widget.ImageView
import com.alibaba.sdk.android.oss.ClientConfiguration
import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback
import com.alibaba.sdk.android.oss.common.OSSLog
import com.alibaba.sdk.android.oss.model.*
import com.dpiinc.TechPage.AIWriteApplication
import com.dpiinc.TechPage.AIWriteSettings
import com.dpiinc.TechPage.AWLog
import com.dpiinc.TechPage.api.ApiConstants
import com.dpiinc.TechPage.constants.FileType
import com.dpiinc.TechPage.oss.Config.endpoint
import java.io.File
import java.io.IOException
import java.util.*
import com.alibaba.sdk.android.oss.model.DeleteObjectRequest
import com.alibaba.sdk.android.oss.model.DeleteObjectResult


class OssFileManager {

    companion object {
        private const val TAG = "OssFileManager"
        private const val CALLBACK_BODY = "userId=\${x:userId}&fileType=\${x:fileType}&noteId=\${x:noteId}&fileName=\${x:fileName}&time=\${x:time}&appId=\${x:appId}"
        val instance = OssFileManager()
    }

    private lateinit var ossClient: OSSClient

    private var uploadSuccessCount = 0
    private var uploadFailedCount = 0

    private var downloadSuccessCount = 0
    private var downloadFailedCount = 0

    init {
        initOSS()
    }

    private fun initOSS() {
        val credentialProvider = AiWriteAuthCredentialsProvider()
        //token会在过期的时候自动获取，但是这个过程会在设置bucket之后，真正上传之前，但是bucket等信息也是通过这个接口获取的，所以先提前调用一次
//        credentialProvider.federationToken
        val conf = ClientConfiguration()
        conf.connectionTimeout = 15 * 1000 // 连接超时，默认15秒
        conf.socketTimeout = 15 * 1000 // socket超时，默认15秒
        conf.maxConcurrentRequest = 5 // 最大并发请求书，默认5个
        conf.maxErrorRetry = 2 // 失败后最大重试次数，默认2次
        ossClient = OSSClient(AIWriteApplication.instance(), endpoint, credentialProvider, conf)
        OSSLog.enableLog()
    }

    fun asyncPutFiles(files: List<UploadTask>, progressCallback: (UploadTask, String, Float, Boolean) -> Unit) {
        uploadSuccessCount = 0
        uploadFailedCount = 0
        files.forEach {
            asyncPutFile(it, progressCallback, files.size)
        }
    }

    fun asyncGetFiles(files: List<DownloadTask>, progressCallback: (DownloadTask, Float, Boolean) -> Unit) {
        downloadSuccessCount = 0
        downloadFailedCount = 0
        files.forEach {
            asyncGetFile(it, progressCallback, files.size)
        }
    }

//    fun asyncPutFile(files: List<UploadTask>) {
//        val uploadObservables = ArrayList<Observable<UploadTask>>()
//
//        files.forEach {
//            uploadObservables.add(Observable.just(it))
////            asyncPutFile(it, files.size)
//        }
//        Observable.zip(uploadObservables) {
//
//        }.subscribeOn(Schedulers.io())
//                .observeOn(AndroidSchedulers.mainThread())
//    }
//
//    // 从本地文件上传，采用阻塞的同步接口
//    fun syncPutFile(fileInfo: UploadTask) {
//        val file = File(fileInfo.filePath)
//        if (!file.exists()) {
//            AWLog.w("AsyncPutFile", "FileNotExist")
//            AWLog.w("LocalFile", file.toString())
//            return
//        }
//        val put = getPutObjectRequest(fileInfo)
//
//        try {
//            val putResult = ossClient.putObject(put)
//            Log.d("PutObject", "UploadSuccess")
//
//            Log.d("ETag", putResult.getETag())
//            Log.d("RequestId", putResult.getRequestId())
//
//        } catch (e: ClientException) {
//            // 本地异常如网络异常等
//            e.printStackTrace()
//        } catch (e: ServiceException) {
//            // 服务异常
//            Log.e("RequestId", e.requestId)
//            Log.e("ErrorCode", e.errorCode)
//            Log.e("HostId", e.hostId)
//            Log.e("RawMessage", e.rawMessage)
//        }
//
//    }

    private fun getPutObjectRequest(task: UploadTask): PutObjectRequest {


        val filePrefix = when (task.fileType) {
            FileType.DOT_FILE -> AIWriteSettings.getOssDotFilePrefix()
            FileType.AUDIO_FILE -> AIWriteSettings.getOssAudioFilePrefix()
            FileType.IMG_FILE -> AIWriteSettings.getOssImgFilePrefix()
            else -> AIWriteSettings.getOssDotFilePrefix()
        }
        val bucket = AIWriteSettings.getOssBucket()

        val objectKey = filePrefix + "/" + task.fileName
        // 构造上传请求
        val put = PutObjectRequest(bucket, objectKey, task.filePath)

        put.crC64 = OSSRequest.CRC64Config.YES
        put.callbackParam = HashMap<String, String>().apply {
            put("callbackUrl", Config.callbackAddress)
            put("callbackBody", CALLBACK_BODY)
        }
        put.callbackVars = HashMap<String, String>().apply {
            put("x:userId", task.userId)
            put("x:fileType", task.fileType.value.toString())
            put("x:noteId", task.noteId)
            put("x:fileName", task.fileName)
            put("x:time", task.time.toString())
            put("x:appId", ApiConstants.appId)
        }
        // 异步上传时可以设置进度回调

        put.progressCallback = OSSProgressCallback { request, currentSize, totalSize ->
            AWLog.d("PutObject", "currentSize: $currentSize totalSize: $totalSize")
            val progress = (100 * currentSize / totalSize).toInt()

            AWLog.d(TAG, "${task.fileName}.....................$progress")
        }
        return put
    }

    fun asyncPutFile(task: UploadTask, progressCallback: (UploadTask, String, Float, Boolean) -> Unit, totalCount: Int = 1) {
        val uploadStart = System.currentTimeMillis()

        val file = File(task.filePath)
        if (!file.exists()) {
            AWLog.w("AsyncPutFile", "FileNotExist")
            AWLog.w("LocalFile", file.toString())
            onUploadSuccess(task, progressCallback, "FileNotExist", totalCount)
            return
        }
        if (file.length() == 0L) {
            AWLog.w(TAG, "empty file")
            onUploadSuccess(task, progressCallback, "empty file", totalCount)
            return
        }

        val put = getPutObjectRequest(task)



        val task = ossClient.asyncPutObject(put, object : OSSCompletedCallback<PutObjectRequest, PutObjectResult> {
            override fun onSuccess(request: PutObjectRequest, result: PutObjectResult) {
                AWLog.i(TAG, "${task.fileName}...UploadSuccess")

                AWLog.d(TAG, result.eTag)
                AWLog.d(TAG, result.requestId)

                val uploadEnd = System.currentTimeMillis()
                OSSLog.logDebug("upload cost: " + (uploadEnd - uploadStart) / 1000f)

                onUploadSuccess(task, progressCallback, result.serverCallbackReturnBody, totalCount)
            }

            override fun onFailure(request: PutObjectRequest, clientExcepion: ClientException?, serviceException: ServiceException?) {
                var info = ""

                // 请求异常
                if (clientExcepion != null) {
                    // 本地异常如网络异常等
                    clientExcepion.printStackTrace()
                    info = clientExcepion.toString()

                }
                if (serviceException != null) {
                    // 服务异常
                    AWLog.e("ErrorCode", serviceException.errorCode)
                    AWLog.e("RequestId", serviceException.requestId)
                    AWLog.e("HostId", serviceException.hostId)
                    AWLog.e("RawMessage", serviceException.rawMessage)
                    info = serviceException.toString()

                }
                onUploadFailed(task, progressCallback, info, totalCount)
            }
        })
    }

    @Synchronized
    private fun onUploadSuccess(task: UploadTask, progressCallback: (UploadTask, String, Float, Boolean) -> Unit, response: String, totalCount: Int) {
        synchronized(uploadSuccessCount) {
            AWLog.i(TAG, "Callback:  + $response")
            uploadSuccessCount++
            progressCallback(task, response, uploadSuccessCount.toFloat() / totalCount, checkIfUploadComplete(totalCount))
        }
    }

    @Synchronized
    private fun onUploadFailed(task: UploadTask, progressCallback: (UploadTask, String, Float, Boolean) -> Unit, response: String, totalCount: Int) {
        synchronized(uploadFailedCount) {
            uploadFailedCount++
            progressCallback(task, response, uploadSuccessCount.toFloat() / totalCount, checkIfUploadComplete(totalCount))
        }
    }

    @Synchronized
    private fun checkIfUploadComplete(totalCount: Int): Boolean {
        return (uploadSuccessCount + uploadFailedCount == totalCount)
    }

    private fun getGetObjectRequest(fileInfo: DownloadTask): GetObjectRequest {
        val bucket = AIWriteSettings.getOssBucket()

        // 构造下载请求
        val get = GetObjectRequest(bucket, fileInfo.objectKey)
        get.crC64 = OSSRequest.CRC64Config.YES
        get.setProgressListener { request, currentSize, totalSize ->
            AWLog.d("GetObject", "currentSize: $currentSize totalSize: $totalSize")
            val progress = (100 * currentSize / totalSize).toInt()
            AWLog.d(TAG, "${fileInfo.objectKey}.....................$progress")
        }
        return get
    }

    fun getObjectSample(fileInfo: DownloadTask, progressCallback: (DownloadTask, Float, Boolean) -> Unit, totalCount: Int = 1) {

        val get = getGetObjectRequest(fileInfo)
        var getResult: GetObjectResult? = null
        try {
            // Download the file in the synchronous way
            getResult = ossClient.getObject(get)
//            callback.onSuccess(get, getResult)
            OSSLog.logDebug("Content-Length", "" + getResult!!.contentLength)

            // Gets the file's input stream.
            val inputStream = getResult!!.objectContent

            val buffer = ByteArray(2048)
            var len = 0

            while (len != -1) {
                len = inputStream.read(buffer)
                // Process the downloaded data, here just print the total length
                OSSLog.logDebug("asyncGetObjectSample", "read length: $len", false)
            }
            OSSLog.logDebug("asyncGetObjectSample", "download success.")

            // Looks up the metadata---it's included in the getResult object.
            val metadata = getResult.metadata
            OSSLog.logDebug("ContentType", metadata.contentType)

        } catch (e: ClientException) {
            // Client side exceptions, such as network exception
            e.printStackTrace()
//            callback.onFailure(get, e, null)
        } catch (e: ServiceException) {
            // Service side exception
            OSSLog.logError("RequestId", e.requestId)
            OSSLog.logError("ErrorCode", e.errorCode)
            OSSLog.logError("HostId", e.hostId)
            OSSLog.logError("RawMessage", e.rawMessage)
//            callback.onFailure(get, null, e)
        } catch (e: IOException) {
            e.printStackTrace()
//            callback.onFailure(get, ClientException(e), null)
        } finally {
            if (getResult != null) {
                try {
                    getResult.objectContent.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }

            }
        }
    }

    fun asyncGetFile(fileInfo: DownloadTask, progressCallback: (DownloadTask, Float, Boolean) -> Unit, totalCount: Int = 1) {
//        launch(CommonPool) {
//            getObjectSample(fileInfo, progressCallback)
//        }
        val getStart = System.currentTimeMillis()
        if (fileInfo.objectKey.isEmpty()) {
            AWLog.w("AsyncGetImage", "ObjectNull")
            //TODO 这里应该调用成功或者失败的回调
            return
        }
        val get = getGetObjectRequest(fileInfo)

        val task = ossClient.asyncGetObject(get, object : OSSCompletedCallback<GetObjectRequest, GetObjectResult> {
            override fun onSuccess(request: GetObjectRequest, result: GetObjectResult) {
                // 请求成功
                val inputStream = result.objectContent
                //Bitmap bm = BitmapFactory.decodeStream(inputStream);

                val targetFile = File(fileInfo.targetFilePath)
                val outputStream = targetFile.outputStream()

                val buffer = ByteArray(2048)
                var len = 0
                try {
                    while (len != -1) {
                        len = inputStream.read(buffer)
                        // Process the downloaded data
                        outputStream.write(buffer)
                        OSSLog.logDebug("asyncGetObjectSample", "read length: $len", false)
                    }
                    outputStream.flush()
                    OSSLog.logDebug("asyncGetObjectSample", "download success.")
                } catch (e: IOException) {
                    e.printStackTrace()
                } finally {
                    try {
                        outputStream.close()
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
                val getEnd = System.currentTimeMillis()
                OSSLog.logDebug("get cost: " + (getEnd - getStart) / 1000f)
                AWLog.d("Bucket: " + Config.DEFAULT_BUCKET + "\nObject: " + request.objectKey + "\nRequestId: " + result.requestId)
                synchronized(downloadSuccessCount) {
                    AWLog.i(TAG, "download complete -> ${fileInfo.objectKey}")
                    downloadSuccessCount++
                    progressCallback(fileInfo, downloadSuccessCount.toFloat() / totalCount, checkIfDownloadComplete(totalCount))
                }
            }

            override fun onFailure(request: GetObjectRequest, clientExcepion: ClientException?, serviceException: ServiceException?) {
                var info = ""
                // 请求异常
                if (clientExcepion != null) {
                    // 本地异常如网络异常等
                    clientExcepion.printStackTrace()
                    info = clientExcepion.toString()
                }
                if (serviceException != null) {
                    // 服务异常
                    AWLog.e("ErrorCode", serviceException.errorCode)
                    AWLog.e("RequestId", serviceException.requestId)
                    AWLog.e("HostId", serviceException.hostId)
                    AWLog.e("RawMessage", serviceException.rawMessage)
                    info = serviceException.toString()
                }
                synchronized(downloadFailedCount) {
                    AWLog.i(TAG, "download failed -> ${fileInfo.objectKey}")
                    downloadFailedCount++
                    progressCallback(fileInfo, downloadSuccessCount.toFloat() / totalCount, checkIfDownloadComplete(totalCount))
                }
            }
        })
    }

    @Synchronized
    private fun checkIfDownloadComplete(totalCount: Int): Boolean {
        return (downloadSuccessCount + downloadFailedCount == totalCount)
    }

    fun loadImage(imageView: ImageView, fileInfo: DownloadTask, progressCallback: (DownloadTask, Float, Boolean) -> Unit, totalCount: Int = 1) {
        val getStart = System.currentTimeMillis()
        if (fileInfo.objectKey.isEmpty()) {
            AWLog.w("AsyncGetImage", "ObjectNull")
            return
        }
        val get = getGetObjectRequest(fileInfo)

        val task = ossClient.asyncGetObject(get, object : OSSCompletedCallback<GetObjectRequest, GetObjectResult> {
            override fun onSuccess(request: GetObjectRequest, result: GetObjectResult) {
                // 请求成功
                val inputStream = result.objectContent
                //Bitmap bm = BitmapFactory.decodeStream(inputStream);

                val targetFile = File(fileInfo.targetFilePath)
                val outputStream = targetFile.outputStream()

                val buffer = ByteArray(2048)
                var len = 0
                try {
                    while (len != -1) {
                        len = inputStream.read(buffer)
                        // Process the downloaded data
                        outputStream.write(buffer)
                        OSSLog.logDebug("asyncGetObjectSample", "read length: $len", false)
                    }
                    outputStream.flush()
                    OSSLog.logDebug("asyncGetObjectSample", "download success.")
                } catch (e: IOException) {
                    e.printStackTrace()
                } finally {
                    try {
                        outputStream.close()
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
                val getEnd = System.currentTimeMillis()
                OSSLog.logDebug("get cost: " + (getEnd - getStart) / 1000f)
                AWLog.d("Bucket: " + Config.DEFAULT_BUCKET + "\nObject: " + request.objectKey + "\nRequestId: " + result.requestId)
                synchronized(downloadSuccessCount) {
                    AWLog.i(TAG, "download complete -> ${fileInfo.objectKey}")
                    downloadSuccessCount++
                    progressCallback(fileInfo, downloadSuccessCount.toFloat() / totalCount, checkIfDownloadComplete(totalCount))
                }
            }

            override fun onFailure(request: GetObjectRequest, clientExcepion: ClientException?, serviceException: ServiceException?) {
                var info = ""
                // 请求异常
                if (clientExcepion != null) {
                    // 本地异常如网络异常等
                    clientExcepion.printStackTrace()
                    info = clientExcepion.toString()
                }
                if (serviceException != null) {
                    // 服务异常
                    AWLog.e("ErrorCode", serviceException.errorCode)
                    AWLog.e("RequestId", serviceException.requestId)
                    AWLog.e("HostId", serviceException.hostId)
                    AWLog.e("RawMessage", serviceException.rawMessage)
                    info = serviceException.toString()
                }
                synchronized(downloadFailedCount) {
                    AWLog.i(TAG, "download failed -> ${fileInfo.objectKey}")
                    downloadFailedCount++
                    progressCallback(fileInfo, downloadFailedCount.toFloat() / totalCount, checkIfDownloadComplete(totalCount))
                }
            }
        })
    }

    fun deleteStroke(task: UploadTask) {


        val filePrefix = when (task.fileType) {
            FileType.DOT_FILE -> AIWriteSettings.getOssDotFilePrefix()
            FileType.AUDIO_FILE -> AIWriteSettings.getOssAudioFilePrefix()
            FileType.IMG_FILE -> AIWriteSettings.getOssImgFilePrefix()
            else -> AIWriteSettings.getOssDotFilePrefix()
        }

        val objectKey = filePrefix + "/" + task.fileName
        // 创建删除请求
        val delete = DeleteObjectRequest(AIWriteSettings.getOssBucket(), objectKey)
        // 异步删除
        val deleteTask = ossClient.asyncDeleteObject(delete, object : OSSCompletedCallback<DeleteObjectRequest, DeleteObjectResult> {
            override fun onSuccess(request: DeleteObjectRequest, result: DeleteObjectResult) {
                Log.d("asyncCopyAndDelObject", "success!")
            }

            override fun onFailure(request: DeleteObjectRequest, clientExcepion: ClientException?, serviceException: ServiceException?) {
                // 请求异常
                clientExcepion?.printStackTrace()
                if (serviceException != null) {
                    // 服务异常
                    Log.e("ErrorCode", serviceException.errorCode)
                    Log.e("RequestId", serviceException.requestId)
                    Log.e("HostId", serviceException.hostId)
                    Log.e("RawMessage", serviceException.rawMessage)
                }
            }
        })
    }
}